#ifndef NIM_SERVER_H_
#define NIM_SERVER_H_

#include "nim-common.h"
#include <map>

#define MIN_NUMBER_OF_PLAYERS 2
#define MAX_NUMBER_OF_PLAYERS 9
#define MAX_NUMBER_OF_CLIENTS 9

#define MAX_NUMBER_OF_TOTAL_CONNECTIONS 25

#define NUMBER_OF_LISTEN_CONNECTIONS 15 // could be 10, but better be on the safe side

#define ARGS_ERROR -1
#define ARGS_VALID 0

#define CONNECTION_ERROR -1
#define CONNECTION_CLIENT_DISCONNECTION -2

#define GAME_END_CONNECTION_ERROR -1
#define GAME_END_VALID 0

#define GAME_MOVE_VALID_STATE 0
#define GAME_MOVE_INVALID_STATE -1

#define CLIENT_MESSAGE_VALID 0
#define CLIENT_MESSAGE_INVALID -1

#define CONNECTION_DISCONNECTED_STATUS -1
#define CONNECTION_VIEWER_STATUS 0
#define CONNECTION_PLAYER_STATUS 1
#define CONNECTION_NEWPLAYER_STATUS 2

#define GAME_END_STATUS_VIEWER 2
#define GAME_END_STATUS_WINNER 1
#define GAME_END_STATUS_LOSER 0

using namespace std;

struct server_parameters {
	int numberOfPlayers;
	int heapSizeLimit;
	bool isMisere;
	int portNumber;
};

struct game_state {
	bool roundBeginningNotificationSent;
	bool gameEnded;
	byte nextClientId;
	int numberOfClients;
	int heapSizeLimit;
	bool isMisere;
	byte numberOfPlayers;
	int32_t heaps[NUMBER_OF_HEAPS];
	list<byte> players;

	/* no connection(-1), viewer(0), player(1) */
	byte connectionStatus[MAX_NUMBER_OF_TOTAL_CONNECTIONS+1];

	int clientSockets[MAX_NUMBER_OF_TOTAL_CONNECTIONS+1];
	Transmission* reciveTranmissions[MAX_NUMBER_OF_TOTAL_CONNECTIONS+1];
	list<Transmission*> sendTranmissions[MAX_NUMBER_OF_TOTAL_CONNECTIONS+1];
	map<int,Transmission*> pendingRejectedClients;
};

int parseCommandLineArguments(int argc, const char** argv, struct server_parameters& params);
int setupServer(int portNumber);

void initGameState(struct game_state& gameState, struct server_parameters& params);
void destroyGameState(struct game_state& gameState);

int manageGame(int sock, struct game_state& gameState);

int receiveTrasnmission(Transmission** transmission, int sock, struct game_state& gameState, byte clientId);
int sendTrasnmission(Transmission& transmission, int sock);

int acceptNewConnection(int sock);

void handleStatusMessages(struct game_state& gameState, int clientId);
void handleRoundBeginning(struct game_state& gameState);
void handleYourTurn(struct game_state& gameState);
void handleGameEnd(struct game_state& gameState, byte lastPlayer);

void handleRejectedClient(int rejectedSock, struct game_state& gameState);
void handleNewClient(int clientSock, struct game_state& gameState);
void handleClientMessage(int opcode, char buffer[], struct game_state& gameState, byte clientId);
void handleClientMoveMessage(char buffer[], struct game_state& gameState, byte clientId);
void handleClientIllegalMoveMessage(char buffer[], struct game_state& gameState, byte clientId);
void handleClientChatMessage(char buffer[], struct game_state& gameState, byte clientId);
void handleClientQuitMessage(char buffer[], struct game_state& gameState, byte clientId);
void handleClientInvalidMessage(char buffer[], struct game_state& gameState, byte clientId);
void handleClientNotInTurnMessage(char buffer[], struct game_state& gameState, byte clientId);

bool isClientTurn(struct game_state& gameState, byte clientId);
bool isPlayer(struct game_state& gameState, byte clientId);
bool isConnected(struct game_state& gameState, byte clientId);

bool isPendingSends(struct game_state& gameState);

void switchCurrentPlayer(struct game_state& gameState);

void removeClient(struct game_state& gameState, byte clientId);

void createBooleanMessage(char buffer[], int opCode, bool boolean);
void createAckMessage(char buffer[], bool isLegalMove);
void createGameTypeMessage(char buffer[], bool isMisere);
void createGameEndStatusMessage(char buffer[], byte status);
void createServerChatMessage(char buffer[], byte sender, int messageLength, char* message);
void createYourTurnMessage(char buffer[]);
void createPlayerStatusMessage(char buffer[], byte numberOfPlayers, byte clientId);
void createPlayerGameStatusMessage(char buffer[], byte status);
void createIllegalNotInTurnMessage(char buffer[]);
void createClientRejectedMessage(char buffer[]);
void createStatusMessage(char buffer[], const int32_t heaps[]);

void populateWithRandomHeaps(struct game_state& gameState);
bool isLegalMove(struct game_state& gameState, int chosenHeap, int32_t numberOfRemovedObjects);
bool isGameEnd(struct game_state& gameState);

#endif /* NIM_SERVER_H_ */
